#include "Program.hpp"

#include "Debug.hpp"

Program::Program() :
  m_handle(0), m_log(0)
{
}

Program::~Program()
{
  if (m_handle) {
    glDeleteProgram(m_handle);
  }
  
  if (m_log) {
    delete [] m_log;
  }
}

void Program::create()
{
  m_handle = glCreateProgram();
}

void Program::attachShader(const Shader * shader)
{
  glAttachShader(m_handle, shader->getHandle());
  m_shaders.insert(shader);

}

void Program::detachShader(const Shader * shader)
{
  glDetachShader(m_handle, shader->getHandle());
  m_shaders.erase(shader);
}

void Program::detachShaders()
{
  Set<const Shader *>::type::const_iterator it;
  for (it = m_shaders.begin(); it != m_shaders.end(); ++it) {
    glDetachShader(m_handle, (*it)->getHandle());
  }
  
  m_shaders.clear();
}

void Program::link()
{
  if (!m_handle) {
    ERROR("Program in invalid state");
    return;
  }
  
  glLinkProgram(m_handle);
  
  GLint linkStatus;
  glGetProgramiv(m_handle, GL_LINK_STATUS, &linkStatus);
  
  GLint infoLogLength;
  glGetProgramiv(m_handle, GL_INFO_LOG_LENGTH, &infoLogLength);
    
  m_log = new GLchar[infoLogLength];
  glGetProgramInfoLog(m_handle, infoLogLength, NULL, m_log);
  
  if (m_log[0] != '\0') {
    DEBUG("Compilation Log:\n%s\n", m_log);
  }
  
  if (linkStatus != GL_TRUE) {
    ERROR("Link failed");
    return;
  }
  
}

void Program::postDelete()
{
  if (m_handle) {
    glDeleteProgram(m_handle);
    m_handle = 0;
  }
  
  if (m_log) {
    delete [] m_log;
    m_log = 0;
  }
  
  m_attributeLocations.clear();
  m_uniformLocations.clear();
  m_shaders.clear();
}

GLint Program::getUniformLocation(const std::string & name)
{
  Map<std::string, GLint>::type::iterator it;
  
  it = m_uniformLocations.find(name);
  
  GLint location;
  
  if (it == m_uniformLocations.end()) { // if name not added
    location = glGetUniformLocation(m_handle, name.c_str());
    m_uniformLocations[name] = location;
  } else {
    location = it->second;
  }
  
  if (location == -1)
      DEBUG("Could not find Uniform: %s\n", name.c_str());
  
  return location;
}

GLint Program::getAttributeLocation(const std::string & name)
{
  
  Map<std::string, GLint>::type::iterator it;
  
  it = m_attributeLocations.find(name);
  
  GLint location;
  
  if (it == m_attributeLocations.end()) { // if name not added
    location = glGetAttribLocation(m_handle, name.c_str());
    m_attributeLocations[name] = location;
  } else {
    location = it->second;
  }
  
  if (location == -1)
      DEBUG("Could not find Attribute: %s\n", name.c_str());
  
  return location;
}

